/**
 * @Author: gaoshiyao
 * @Description: db
 * @File:  db
 * @Date: 2021/05/08 14:33
 */

package utils

import (
	"errors"
	"fmt"
	log "github.com/sirupsen/logrus"
	env "qabx.net/safe-app-center/pkg/config/env"
	"time"

	_ "github.com/lib/pq"
	"github.com/xormplus/xorm"
	"github.com/xormplus/xorm/names"
	//"gitlab.wodcloud.com/nj-gjdw/grid-data-pull/src/common/conf"
	"go.uber.org/zap"
)

var dbCli *xorm.Engine
var err error

type Pg struct {
	Client      *xorm.Engine
	session     *xorm.Session
	Schema      string
	UpdateParam UpdateParam
	DeleteParam DeleteParam
	InsertParam InsertParam
	QueryParam  QueryParam
}

type QueryParam struct {
	Resource interface{}
	Where    string
	Join     [][4]string
	Page     int
	PageSize int
	OrderBy  string
	GroupBy  string
	Alias    string
	Table    string
	Unscoped bool //是否查询软性删除的数据
	Cols     []string
}

type UpdateParam struct {
	Resource interface{}
	Where    string
	Table    string
	Id       int64
	Cols     []string
}

type DeleteParam struct {
	Resource interface{}
	Where    string
	Table    string
	Unscoped bool //是否进行硬性删除 软删除需要配合 deleted标识
}

type InsertParam struct {
	Resource interface{}
	Table    string
}

func Init() {
	if dbCli != nil {
		return
	}

	url := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
		env.DbOptions.DbHost, env.DbOptions.DbPort, env.DbOptions.DbUsername, env.DbOptions.DbPassword, env.DbOptions.DbDatabase)

	dbCli, err = xorm.NewEngine(env.DbOptions.DbDriver, url)
	if err != nil {
		log.Error("创建xorm引擎", zap.Error(err))
		return
	}
	if err = dbCli.Ping(); err != nil {
		log.Error("ping db", zap.Error(err))
		return
	}
	dbCli.SetMapper(names.SnakeMapper{})
	if env.DbOptions.RunMode == "debug" {
		dbCli.ShowSQL(true)
	}
	dbCli.SetTZLocation(time.UTC)
	dbCli.SetMaxOpenConns(100) // 数据库连接数
	dbCli.SetMaxIdleConns(10)  // 空闲数
	dbCli.SetConnMaxLifetime(5 * time.Minute)
	log.Infof("connect db success dbHost:%s,dbDatabase:%s", env.DbOptions.DbHost+":"+env.DbOptions.DbPort, env.DbOptions.DbDatabase)
	return
}

func GetDbClient() (client Pg, err error) {
	client.Schema = env.DbOptions.DbSchema
	if dbCli != nil {
		client.Client = dbCli
		return
	}
	err = errors.New("get connect client err")
	return
}

// 开启事务
func (p *Pg) StartTransaction() {
	p.session = p.Client.NewSession()
	err := p.session.Begin()
	if err != nil {
		err = errors.New("start transaction err")
		return
	}
}

// 事务提交
func (p *Pg) CommitTransaction() error {
	//提交事务
	if p.session != nil {
		err := p.session.Commit()
		if err != nil {
			return errors.New("commit transaction err : " + err.Error())
		}
		defer func() {
			//事务关闭
			_ = p.session.Close()
			//将程序的事务置为nil
			p.session = nil
		}()
	}
	return nil
}

// 事务回退
func (p *Pg) RollbackTransaction() error {
	if p.session != nil {
		err := p.session.Rollback()
		if err != nil {
			return errors.New("rollback err : " + err.Error())
		}
		defer func() {
			//事务关闭
			_ = p.session.Close()
			//将程序的事务置为nil
			p.session = nil
		}()
	}
	return nil
}

// 根据是否有事务来连接表数据
func (p *Pg) GetTableSession(table string) *xorm.Session {
	if p.Schema != "" {
		table = p.Schema + "." + table
	}
	if p.session != nil {
		return p.session.Table(table)
	}
	return p.Client.Table(table)
}

// 添加操作
func (p *Pg) Insert() (res int64, err error) {
	res, err = p.GetTableSession(p.InsertParam.Table).Insert(p.InsertParam.Resource)
	if err != nil {
		fmt.Println(err)
	}
	return
}

// 修改操作
func (p *Pg) Update() (res int64, err error) {
	session := p.GetTableSession(p.UpdateParam.Table)
	if p.UpdateParam.Id > 0 {
		session = session.ID(p.UpdateParam.Id)
	}
	if p.UpdateParam.Where != "" {
		session = session.Where(p.UpdateParam.Where)
	}
	res, err = session.Cols(p.UpdateParam.Cols...).Update(p.UpdateParam.Resource)
	if err != nil {
		fmt.Println(err)
	}
	return
}

// 查询列表
func (p *Pg) Select() (err error) {
	session := p.queryCommon()
	if p.QueryParam.PageSize > 0 && p.QueryParam.Page > 0 {
		session = session.Limit(p.QueryParam.PageSize, (p.QueryParam.Page-1)*(p.QueryParam.PageSize))
	}
	err = session.Find(p.QueryParam.Resource)
	if err != nil {
		fmt.Println(err)
	}
	return
}

// 查询单条
func (p *Pg) GetOne() (res bool, err error) {
	session := p.queryCommon()
	if p.QueryParam.PageSize > 0 && p.QueryParam.Page > 0 {
		session = session.Limit(p.QueryParam.PageSize, (p.QueryParam.Page-1)*(p.QueryParam.PageSize))
	}
	res, err = session.Get(p.QueryParam.Resource)
	if err != nil {
		fmt.Println(err)
	}
	return
}

func (p *Pg) Delete() (res int64, err error) {
	session := p.GetTableSession(p.DeleteParam.Table)
	if p.DeleteParam.Unscoped == true { //是否进行硬性删除
		session = session.Unscoped()
	}
	res, err = session.Where(p.DeleteParam.Where).Delete(p.DeleteParam.Resource)
	return
}

// 公共查询方法
func (p *Pg) queryCommon() (session *xorm.Session) {
	session = p.GetTableSession(p.QueryParam.Table)
	session = session.Where(p.QueryParam.Where)
	if len(p.QueryParam.Join) > 0 {
		for _, v := range p.QueryParam.Join {
			session = session.Join(v[0], v[1]+" as "+v[2], v[3])
		}
	}
	if p.QueryParam.Alias != "" {
		session = session.Alias(p.QueryParam.Alias)
	}
	if p.QueryParam.OrderBy != "" {
		session = session.OrderBy(p.QueryParam.OrderBy)
	}
	if p.QueryParam.GroupBy != "" {
		session = session.GroupBy(p.QueryParam.GroupBy)
	}
	if len(p.QueryParam.Cols) > 0 {
		session = session.Cols(p.QueryParam.Cols...)
	}
	return
}
